import timber.data.XMLDataNode;


/**
	XMLData class,
	Parses an XML file into "legible" data structures.
	Multiple children of the same name on the same level are treated as arrays,
	tags are considered objects in the tree hierarchy, tag attributes are added as 
	properties of the corresponding tag object and the tag's value, if it's a text node,
	is available in the _value property.
	
	Technically each tag is made into a XMLDataNode object, which will behave like an array
	if the length property is accessed. This allows nodes which at some point only 
	have 1 same-named sibling to behave as arrays for the possibility of other children
	being added later.

	@see XMLDataNode.
	
	
	@implementationNote This class requires the XMLDataNode class.
	@implementationNote Private properties are prefixed with $ to hide from the parsing.
	
	
	@author  Geoff Stearns (geoff@deconcept.com)
	@version 2005-06-08


*/

class timber.data.XMLData extends XMLDataNode
{
	private var $loaded:Boolean = false;
	private var $callback;
	private var $thexml:XML;
	private var $root:String;

   public function XMLData(dataURL, aCallback, method) {
		// this way you should be able to use the old syntax
		if (method) {
			$callback = mx.utils.Delegate.create(aCallback, aCallback[method]);
		} else {
			$callback = aCallback;
		}
		
		$thexml = new XML();
		$thexml.ignoreWhite = true;
		var self = this;
		$thexml.onLoad = function(s) {
			if (s) {
				self.$loaded = true;
				// create our data object we'll use
				self.$root = this.firstChild.nodeName;
				self.makeObj(null, self);
			}
			self.$callback(s, self);
		}

		// if dataURL is an object, parse the source property as a string
		if (dataURL instanceof Object) {
			$thexml.parseXML(dataURL.source);
			var myxml = $thexml;
			var r = timber.util.Timer.callLater(1, function() { myxml.onLoad(true); });
		} else {
			$thexml.load(dataURL);
		}
   }
	
	public static function createWithString(str) {
		var x = new XMLData({source:str});
	}
	
	private function makeObj(xObj:XMLNode, obj:Object) {
		//----- Initialise objects
		if(xObj==null) xObj = this.$thexml;
		if(obj==null)  obj = new XMLDataNode();
		var a:Object, c:Object, nName:String, nType:Number, nValue:String, cCount:Number;
		var xmldatatype = false;

		//----- Add attributes to the object
		for(a in xObj.attributes) {
			// if type was set we store it
			if (a == "xmldata:type") {
				xmldatatype = xObj.attributes[a];
				continue;
			}
			obj[a] = xObj.attributes[a];
		}

		//----- Build child nodes
		for(c in xObj.childNodes) {
			nName = xObj.childNodes[c].nodeName;
			nType = xObj.childNodes[c].nodeType;
			nValue = xObj.childNodes[c].nodeValue;

			if(nType==3) {
				if (xmldatatype === false) {
					obj._value = nValue;
					obj._type = 'text';
				} else {
					obj = convertDataType(nValue, xmldatatype);
				}
			}

			if(nType==1 && nName!=null) {
				if (xmldatatype !== false) {
					var convobj = convertDataType(nValue, xmldatatype);
					if ((convobj instanceof Object) && !(convobj instanceof MovieClip)) {
						for (var i in obj) {
							convobj[i] = obj[i];
						}
						obj = convobj;
					}
				}

				if(obj[nName]==null) {
					obj[nName] = this.makeObj(xObj.childNodes[c], new XMLDataNode());
					if (!(obj instanceof XMLDataNode) && (obj[nName]._type != 'array')) obj[nName] = (obj[nName] instanceof XMLDataNode)?obj[nName]._value:obj[nName];
					
				} else if(obj[nName]._type!='array') {
					obj[nName] = [ obj[nName] ];
					obj[nName]._type = 'array';
				}
				if(obj[nName]._type=='array') {
					obj[nName].unshift( this.makeObj(xObj.childNodes[c], new XMLDataNode()) );
				}
			}
		}
		// Return object
		return obj;
	}
	
	public function get loaded() {
		return $loaded;
	}

	public function toString() {
		var out =  "[object XMLData]" + newline;
		out += $thexml.toString();
		return out;
	}
	
	public function convertDataType(value:String, type:String){
		switch ( type.toLowerCase() ) {
			case "color" :
			case "number" :
			case "hex" :
				if (value.indexOf('#') == 0) value = '0x'+value.substr(1);
				return parseInt(value);
				break;
				
			case "boolean" :
				if (value === '0') return false;
				if (value === '1') return true;
				if (value.toLowerCase() === 'false') return false;
				if (value.toLowerCase() === 'true') return true;
				return Boolean(value);
				break;
				
			case "movieclip" :
				var e = eval(value);
				if (!e) {
					var path = value.split('.');
					var fullpath = path[0];
					for (var i=1; i<path.length; i++) {
						e = eval(fullpath+'.'+path[i]);
						if (!e) {
							e = eval(fullpath);
							e = e.createEmptyMovieClip(path[i], e.getNextHighestDepth());
						}
						fullpath = fullpath+'.'+path[i];
					}
				}
				return e;
				break;

			default :
				// see if there is a class with this name, if so create a new one
				if (_global[type]) return new _global[type](value);
				
				// otherwise return the value
				return value;
				break;
		}
	};
	
}
